// Copyright 2019 The rhyme_lph Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// M [author] rhyme_lph
// i [email]  rhymelph@gmail.com
// H [github] https://github.com/rhymelph

import 'package:base_plugin/base_plugin.dart';
import 'package:finance_app/generated/l10n.dart';
import 'package:finance_app/src/common/config.dart';
import 'package:finance_app/src/entities/employee_contact_entity.dart';
import 'package:finance_app/src/entities/leave_type_entity.dart';
import 'package:finance_app/src/entities/ocr_attachment_entity.dart';
import 'package:finance_app/src/http_service/http_employee_contact_service.dart';
import 'package:finance_app/src/http_service/http_leave_service.dart';
import 'package:finance_app/src/mixin/from_attach_mixin.dart';
import 'package:finance_app/src/mixin/init_edit_data_mixin.dart';
import 'package:finance_app/src/page/hr_payroll/leave/new_leave_page.dart';
import 'package:finance_app/src/provider/dashboard_provider.dart';
import 'package:finance_app/src/provider/hr_payroll/views/employee_provider.dart';
import 'package:finance_app/src/provider/hr_payroll/views/leave_provider.dart';
import 'package:finance_app/src/router_gen/override_route_providers.dart';
import 'package:finance_app/src/router_gen/router_gen_providers.dart';
import 'package:finance_app/src/utils/date_time_utils.dart';
import 'package:finance_app/src/utils/permission_utils.dart';
import 'package:finance_app/src/widgets/bottom_sheet/calendar_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/delete_info_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/got_it_tip_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/text_area_input_bottom_sheet.dart';
import 'package:flutter/material.dart';

import '../hr_and_payroll_provider.dart';

class NewLeaveProvider extends BaseProvider<LeaveItemEntity>
    with InitEditDataMixin, FormAttachMixin {
  List<String> timing = ['AM', 'PM'];

  DateTime startDate;

  String startString;

  DateTime endDate;

  String endString;

  TextEditingController notesController = TextEditingController();
  FocusNode notesFocusNode = FocusNode();

  List<String> menuList = ['Approve', 'Reject', 'Delete'];

  bool get hasError => leaveData != null ? leaverDay > leaveData.amount : false;

  @override
  Future get dataFuture => id != null ? HttpLeaveService.showLeave(id) : null;

  EmployeeContactEntity employee;
  EmployeeContactEmployeeLeaves leaveData;

  LeaveTypeEntity leaveType;

  // num get leaverDay {
  //   num otherDay = startString == 'AM' && endString == 'PM' ? 1 : 0;
  //   otherDay = startString == endString ? 0.5 : otherDay;
  //
  //   if (startDate != null && endDate != null) {
  //     Duration duration = endDate.difference(startDate);
  //     if (duration.inDays == 0) {
  //       if (startString == 'AM' && endString == 'PM') return 1;
  //       if (startString == endString) return 0.5;
  //       return 0;
  //     }
  //     return duration.inDays + otherDay;
  //   }
  //   return 0;
  // }
  num leaverDay = 0;

  @override
  void dispose() {
    super.dispose();
    notesController.dispose();
    notesFocusNode.dispose();
  }

  void onSave() async {
    unFocus();
    if (hasError == true) return;

    if (employee == null) {
      showToast(S.current.please_select_employee);
      return;
    }
    if (leaveData == null) {
      showToast(S.current.please_select_leave_type);
      return;
    }
    if (startDate == null) {
      showToast(S.current.please_select_start_date);
      return;
    }
    if (startString == null) {
      showToast(S.current.please_select_start_date_meridian);
      return;
    }
    if (endDate == null) {
      showToast(S.current.please_select_end_date);
      return;
    }
    if (endString == null) {
      showToast(S.current.please_select_end_date_meridian);
      return;
    }

    List<int> attachmentIds = await uploadFiles();
    if (attachmentIds == null) return;

    LeaveItemEntity entity;
    if (id != null) {
      await post(
          HttpLeaveService.updateApplyLeave(
            id,
            employee.id,
            leaveType?.id,
            DateTimeUtils.formatDateTime(startDate, 'yyyy-MM-dd'),
            startString,
            DateTimeUtils.formatDateTime(endDate, 'yyyy-MM-dd'),
            endString,
            notesController.text,
            attachmentIds,
          ),
          autoHandler: AutoHandler.Toast, onSuccess: (result) {
        entity = getEntityFromResult<LeaveItemEntity>(result);
      });
    } else {
      await post(
          HttpLeaveService.applyLeave(
            employee.id,
            leaveType?.id,
            DateTimeUtils.formatDateTime(startDate, 'yyyy-MM-dd'),
            startString,
            DateTimeUtils.formatDateTime(endDate, 'yyyy-MM-dd'),
            endString,
            notesController.text,
            attachmentIds,
          ),
          autoHandler: AutoHandler.Toast, onSuccess: (result) {
        entity = getEntityFromResult<LeaveItemEntity>(result);
      });
    }
    obtainContext?.call((context) {
      if (entity != null) {
        refreshPreviousPage();
        Navigator.of(context).pop(entity);
      }
    });
  }

  void onChangeStartDate() {
    unFocus();
    obtainContext?.call((context) async {
      final DateTime dateTime = await showCalendarBottomSheet(
          context, startDate,
          title: 'Start Date', endDate: endDate);
      if (dateTime != null) {
        startDate = dateTime;
        update();
        onCalculateLeaveDays();
      }
    });
  }

  void onCalculateLeaveDays() {
    if (employee != null &&
        startDate != null &&
        endDate != null &&
        startString != null &&
        endString != null) {
      post(
          HttpLeaveService.calculateLeaveDays(
              employee.id,
              DateTimeUtils.formatDateTime(startDate, 'yyyy-MM-dd'),
              startString,
              DateTimeUtils.formatDateTime(endDate, 'yyyy-MM-dd'),
              endString),
          autoHandler: AutoHandler.Toast, onSuccess: (result) {
        leaverDay = result.data['data']['days'];
        update();
      });
    }
  }

  void onChangeEndDate() {
    unFocus();
    obtainContext?.call((context) async {
      final DateTime dateTime = await showCalendarBottomSheet(context, endDate,
          title: 'End Date', startDate: startDate);
      if (dateTime != null) {
        endDate = dateTime;
        update();
        onCalculateLeaveDays();
      }
    });
  }

  @override
  void unFocus() {
    obtainContext?.call((context) {
      FocusScope.of(context).requestFocus(FocusNode());
    });
  }

  void onSelectStart(String value) {
    unFocus();
    if (startString == value) return;
    startString = value;
    onCalculateLeaveDays();
    update();
  }

  void onSelectEnd(String value) {
    unFocus();
    if (endString == value) return;
    endString = value;
    onCalculateLeaveDays();
    update();
  }

  void onSelectEmployee() async {
    unFocus();
    final result = await Routes.navigateTo(
        RouterGenProviders.employeeContactsPage,
        params: {
          'select': employee,
        });
    if (result != null) {
      employee = result;
      leaveData = null;
      leaveType = null;
      update();
      onCalculateLeaveDays();
    }
  }

  void onSelectLeaveType() async {
    unFocus();
    if (employee == null) {
      showToast(S.current.please_select_employee);
      return;
    }
    final result = await Routes.navigateTo(
        RouterGenProviders.selectLeaveTypePage,
        params: {
          'data': employee,
          'select': leaveData,
        });
    if (result != null && result is EmployeeContactEmployeeLeaves) {
      leaveData = result;
      leaveType = Config.settingData.leaveType.firstWhere(
          (element) => leaveData.type == element.key,
          orElse: () => null);
      update();
    }
  }

  String statusType;
  String rejectReason;

  void initData(LeaveItemEntity data) async {
    if (data == null) {
      await Future.delayed(Duration(milliseconds: 500));
      employee = await Config.getEmployeeMe(this, true, true);
      update();
      return;
    }
    init(data.id);
    employee = EmployeeContactEntity()
      ..id = data.employeeId
      ..name = data.employeeName;
    leaveData = EmployeeContactEmployeeLeaves()
      ..id = data.leaveTypeId
      ..name = data.leaveTypeName
      ..amount = data.days;
    leaveType = LeaveTypeEntity()
      ..id = data.leaveTypeId
      ..name = data.leaveTypeName;
    startDate = DateTimeUtils.toDateTime(data.startDate, 'yyyy-MM-dd');
    startString = data.startDateMeridian;
    endDate = DateTimeUtils.toDateTime(data.endDate, 'yyyy-MM-dd');
    endString = data.endDateMeridian;
    notesController.text = data.notes;
    statusType = data.status;
    rejectReason = data.rejectedReason;
    attachments = data.attachments;

    if (data.status == 'open') {
      if (Config.hasPermission(PermissionUtils.hrPayroll)) {
        menuList = [
          // 'Edit',
          'Approve', 'Reject', 'Delete'
        ];
      } else {
        menuList = ['Delete'];
      }

      // if (data.employeeId != Config.userProfile.employeeId) {
      //   menuList.remove('Delete');
      // }
    } else {
      menuList = [];

      if (data.status == 'approved') {
        menuList = ['Withdraw'];
      }
    }
  }

  @override
  FutureOr<LeaveItemEntity> transformData(
      LeaveItemEntity d, bool isCache) async {
    initData(d);
    return super.transformData(d, isCache);
  }

  void onSelectMenu(String value) {
    //['Approve', 'Reject']
    switch (value) {
      case 'Approve':
        onApprove();
        break;
      case 'Reject':
        onReject();
        break;
      case 'Delete':
        onDelete();
        break;
      case 'Withdraw':
        onWithdraw();
        break;
      case 'Edit':
        if (employee.leaveData == null) {
          post(HttpEmployeeContactService.showContact(employee.id),
              autoHandler: AutoHandler.Toast,
              loadingTip: S.current.loading, onSuccess: (result) {
            employee = getEntityFromResult<EmployeeContactEntity>(result);
            final finderLeave = employee.employeeLeaves.firstWhere(
                (element) => element.name == leaveData.name,
                orElse: () => null);
            if (finderLeave != null) {
              leaveData = finderLeave;
            }
            onChangeEditMode();
          });
        }
        break;
    }
  }

  void onApprove() {
    obtainContext?.call((context) async {
      final result = await showApproveLeaveBottomSheet(context);
      if (result == true) {
        bool isSuccess = false;
        await post(HttpLeaveService.leaveApprove(id),
            loadingTip: S.current.loading,
            autoHandler: AutoHandler.Toast, onSuccess: (result) {
          isSuccess = true;
        });
        if (isSuccess == true) {
          await showLeaveApprovedSuccessBottomSheet(context);
          statusType = 'approved';
          menuList = ['Withdraw'];
          update();
          final hrAndPayroll = Providers.get<HrAndPayrollProvider>(
              RouterGenProviders.hrAndPayrollPage, null);
          if (hrAndPayroll != null) {
            hrAndPayroll.getChildProvider<LeaveProvider>()
              ..updateStatus(id, 'approved')
              ..onRefreshDashboard();
            hrAndPayroll.loadEmployeeDots();
          }
          if (!Config.hasPermission(PermissionUtils.homeDashboard)) {
            final dashboardPayroll = Config.dashboardProvider
                ?.getChildProvider<HrAndPayrollProvider>();
            if (dashboardPayroll != null) {
              dashboardPayroll.getChildProvider<LeaveProvider>()
                ..updateStatus(id, 'approved')
                ..onRefreshDashboard();
              dashboardPayroll.loadEmployeeDots();
            }
          }
          refreshPreviousPage();
        }
      }
    });
  }

  void onReject() {
    obtainContext?.call((context) async {
      String content =
          await showRejectLeaveRequestInputBottomSheet(context, onDone: (data) {
        Navigator.of(context).pop(data);
      });
      if (content != null) {
        bool isSuccess = false;
        await post(HttpLeaveService.leaveReject(id, content),
            loadingTip: S.current.loading,
            autoHandler: AutoHandler.Toast, onSuccess: (result) {
          isSuccess = true;
        });
        if (isSuccess == true) {
          await showLeaveRejectedSuccessBottomSheet(context);
          statusType = 'rejected';
          menuList = [];
          rejectReason = content;
          update();
          refreshPreviousPage();
        }
      }
    });
  }

  @override
  List<OcrAttachmentEntity> attachments;

  void onDelete() {
    obtainContext?.call((context) async {
      final result = await showDeleteLeaveBottomSheet(context);
      if (result == true) {
        bool isSuccess = false;
        await post(HttpLeaveService.batchLeaveDelete([id]),
            autoHandler: AutoHandler.Toast,
            loadingTip: S.current.loading, onSuccess: (result) {
          isSuccess = true;
        });
        if (isSuccess) {
          await showLeaveDeleteSuccessBottomSheet(context);
          Providers.get<HrAndPayrollProvider>(
                  RouterGenProviders.hrAndPayrollPage, null)
              ?.getChildProvider<LeaveProvider>()
              ?.deleteItem(id);
          if (!Config.hasPermission(PermissionUtils.homeDashboard))
            Config.dashboardProvider
                ?.getChildProvider<HrAndPayrollProvider>()
                ?.getChildProvider<LeaveProvider>()
                ?.deleteItem(id);
          Navigator.of(context).pop(true);
        }
      }
    });
  }

  void onWithdraw() {
    obtainContext?.call((context) async {
      final result = await showWithdrawLeaveBottomSheet(context);
      if (result == true) {
        bool isSuccess = false;
        await post(HttpLeaveService.withdrawLeave(id),
            autoHandler: AutoHandler.Toast,
            loadingTip: S.current.loading, onSuccess: (result) {
          isSuccess = true;
        });
        if (isSuccess) {
          await showLeaveWithdrawSuccessBottomSheet(context);
          Providers.get<HrAndPayrollProvider>(
                  RouterGenProviders.hrAndPayrollPage, null)
              ?.getChildProvider<LeaveProvider>()
              ?.updateStatus(id, 'withdrew');
          if (!Config.hasPermission(PermissionUtils.homeDashboard))
            Config.dashboardProvider
                ?.getChildProvider<HrAndPayrollProvider>()
                ?.getChildProvider<LeaveProvider>()
                ?.updateStatus(id, 'withdrew');
          Navigator.of(context).pop();
        }
      }
    });
  }

  @override
  Future get editDataFuture => HttpLeaveService.showLeave(id);

  @override
  TextEditingController get numberCtl => null;

  @override
  String get type => null;

  void refreshPreviousPage() {
    Providers.get<HrAndPayrollProvider>(
            RouterGenProviders.hrAndPayrollPage, null)
        ?.getChildProvider<LeaveProvider>()
        ?.refreshController
        ?.requestRefresh();
    if (!Config.hasPermission(PermissionUtils.homeDashboard))
      Config.dashboardProvider
          ?.getChildProvider<HrAndPayrollProvider>()
          ?.getChildProvider<LeaveProvider>()
          ?.refreshController
          ?.requestRefresh();
  }
}
